package cn.how2go.net.server;

import cn.how2go.net.server.base.TimerList;
import cn.how2go.net.server.codec.Decoder4LoggingOnly;
import cn.how2go.net.server.codec.ProtocolDecoder;
import cn.how2go.net.server.codec.ProtocolEncoder;
import cn.how2go.net.server.handler.NetServerHandler;
import cn.how2go.net.server.service.BreakLinkService;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.Timer;
import java.util.TimerTask;

/**
 *
 */
@Service
public class NetServer{
    public Logger logger = LoggerFactory.getLogger(NetServer.class);

    private String serverIp;
    private int serverPort;

    private static final int MAX_FRAME_LENGTH = 1024 * 1024;
    //表示数据长度字段开始的偏移量
    private static final int LENGTH_FIELD_OFFSET = 3;
    //数据长度字段的所占的字节数
    private static final int LENGTH_FIELD_LENGTH = 2;

    private static final int LENGTH_ADJUSTMENT = 10;
    private static final int INITIAL_BYTES_TO_STRIP = 0;

    /** 用于分配处理业务线程的线程组个数 */
    protected static final int BIZGROUPSIZE = Runtime.getRuntime().availableProcessors() * 2; // 默认
    /** 业务出现线程大小 */
    protected static final int BIZTHREADSIZE = 4;

    ServerBootstrap boot = null;

    private  EventLoopGroup bossGroup = null;
    private  EventLoopGroup workerGroup = null;
    public NetServer(){
        this.serverIp = "0.0.0.0";
        this.serverPort = 11023;
    }

    public NetServer(String serverIp, int serverPort){
        this.serverIp = serverIp;
        this.serverPort = serverPort;
    }


    public void startd()  {
        logger.info("服务器开始启动......");
        bossGroup = new NioEventLoopGroup(BIZGROUPSIZE);
        workerGroup = new NioEventLoopGroup(BIZTHREADSIZE);
        boot = new ServerBootstrap();
        boot.group(bossGroup, workerGroup);
        boot.channel(NioServerSocketChannel.class);

        boot.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline pipeline = ch.pipeline();
                //传输过来的报文日志
                pipeline.addLast(new Decoder4LoggingOnly());
                // 1024表示单条消息的最大长度，解码器在查找分隔符的时候，达到该长度还没找到的话会抛异常
                //防止乱报文阻塞报文，导致终端经常断开
                pipeline.addLast(
                        new DelimiterBasedFrameDecoder(1024,true,false, Unpooled.copiedBuffer(new byte[] { (byte)0x7e}),
                                Unpooled.copiedBuffer(new byte[] { (byte)0x7e})));
                //拦截到正常报文送到Handler去处理
                pipeline.addLast("decoder",new ProtocolDecoder(MAX_FRAME_LENGTH,
                        LENGTH_FIELD_OFFSET,
                        LENGTH_FIELD_LENGTH,
                        LENGTH_ADJUSTMENT,
                        INITIAL_BYTES_TO_STRIP));
                pipeline.addLast("encoder",new ProtocolEncoder());
                pipeline.addLast(new NetServerHandler());
            }
        });
        /**
         * 启动服务器
         */
        try {
            boot.bind(serverIp, serverPort).sync();
            logger.info("服务器启动成功，监听端口:"+serverPort);
        } catch (Exception e) {
            logger.error("==服务器启动失败！！--"+e.getMessage());
        }

        /**
         * 清除需要清除的连接
         */
        if(TimerList.BREAKLINK_TIMER == null) {
            TimerList.BREAKLINK_TIMER = new Timer();
            TimerList.BREAKLINK_TIMER.schedule(new TimerTask() {
                @Override
                public void run() {
                    BreakLinkService.clearOffLine();
                }
            }, 0, 10000);
        }

    }

    public  void shutdown() {
        logger.info("服务器开始停止......");
        if(TimerList.BREAKLINK_TIMER != null) {
            BreakLinkService.clearAllLink();
            TimerList.BREAKLINK_TIMER.cancel();
            TimerList.BREAKLINK_TIMER = null;
        }
        //停止线程
        try {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }catch (Exception e){
            logger.error(e.getMessage());
        }finally {
            workerGroup = null;
            bossGroup = null;
            boot = null;
        }
        logger.info("服务器停止监听端口:"+serverPort);
    }
}
